#!/usr/bin/env python
#-*- coding: utf-8 -*-

from node import node
from edge import edge
from route import route
from graphoid import graphoid
from endOfSanity import *
import time
import math
import config

class graph(graphoid):

    def __init__(self, fileWithDataName=None, g = None):
        
        if fileWithDataName is not None:
            g = graphoid(fileWithDataName)

        self.listOfCars = []         
        self.listOfEdges = []
        self.listOfRoutes = []
        self.listOfNodes = []
        self.time = time.time()
        self.startTime = time.time()
        
        dictonaryNode = {}
        
        for basicNode in g.listOfNodes:
            newNode = node.fromBasicNode(basicNode)
            self.listOfNodes.append(newNode)
            dictonaryNode[basicNode] = newNode
            
        dictionaryEdges = {}
        
        for basicEdge in g.listOfEdges:
            newEdge = edge.fromBasicEdge(basicEdge, dictonaryNode)
            self.listOfEdges.append(newEdge)
            dictionaryEdges[basicEdge] = newEdge
            
        for basicRoute in g.listOfRoutes:
            self.listOfRoutes.append( route.fromBasicRoute(basicRoute,dictionaryEdges) )
            
        self.deltaT = config.deltaT
        
        self.paused = True #kte: tu ma byc True, bo nie chce zeby mi symulacja startowala od razu po stworzeniu grafu
            


    def __repr__(self):
        returnValue = ""
        for elem in self.listOfNodes:
            returnValue += str(elem) + "\n"
        for elem in self.listOfEdges:
            returnValue += str(elem) + "\n"
        for elem in self.listOfCars:
            returnValue += str(elem) + "\n"
            
        return returnValue
        
        
    def addCars(self, car):
        self.listOfCars.append(car)
        
    def getCars(self):
        return self.listOfCars

    def update(self,newTime):
        boolean = False
        while self.time + self.deltaT <= newTime:
            self.nextStep()
            self.time += self.deltaT
            boolean = True
        return boolean
            

    def start(self):
        self.paused = False
    
    def pause(self):
        self.paused = True
            
            
    def nextStep(self):
        
        if not self.paused:
            try:
                #self.sanityCheck()
                
                for node in self.listOfNodes:
                    node.cancelReservation()
        
                for edge in self.listOfEdges:
                    edge.occupancy()

                for car in self.listOfCars:
                    if car.end:
                        self.listOfCars.remove(car)
                
                for car in self.listOfCars:
                    self.makeReservation(car)
          
                for car in self.listOfCars:
                    self.move(car)
            
                for route in self.listOfRoutes:
                    route.creteCar()
            
                deltaTime = time.time() - self.startTime
          
                for node in self.listOfNodes:
                    car = node.addCarFromQueue()
                    node.changeLights( deltaTime )
                    if car is not None:
                        self.listOfCars.append(car)

                #print self.getStatistics()
                    
            except endOfSanity, ex:
                print repr(ex)
                self.paused = True
                
                
    def makeReservation(self,car):
        car.madeReservation = False
        if car.whenWillItMakeReservation > 0:
            return
        #listOfNodesForCar = car.nextNodesTillSafeNode()
        listOfNodesForCar = car.nextFewNodes(20)
        for node in listOfNodesForCar:
            if not ( node.makeReservation(car) or node.isSomeoneInside == car ):
                self.unreserve(car)
            elif node.isRedLight:
                self.unreserve(car)
            car.madeReservation = True
        
    def unreserve(self,car):
        listOfNodesForCar = car.nextNodesTillSafeNode()
        for node in listOfNodesForCar:
            node.revertReservation(car)                
            
    def move(self,car):
        car.updateRouteTime( time.time() )
        if car.end:
            car.route.wyjechane += 1
            self.listOfCars.remove(car)
            return 
        if car.whenWillItMakeReservation > 0:
            car.whenWillItMakeReservation -= 1
            return
        goodReservation = True
            
        for node in car.nextNodesTillSafeNode():
            if node.isRedLight:
                assert goodReservation 
                break
            if node.reservation is None:
                goodReservation = False
                break
                
            if car.route.getEdge(node) == node.reservation.route.getEdge(node)\
             and node not in node.reservation.jumpedIntoTheQueue:
                continue
            if car == node.reservation:
                print 4
                continue
            if node.isSomeoneInside == car:
                continue
            
            if len( node.reservation.jumpedIntoTheQueue ) > 0:
                print node.reservation.jumpedIntoTheQueue, node in node.reservation.jumpedIntoTheQueue
            if not goodReservation:
                break
                
        if goodReservation:
            speed = car.checkColision( config.carCollisionCheckSteps )
            car.move(speed)
        elif car.madeReservation:
            r = car.edge.length() - car.position  - (car.radius +
                car.edge.endNode.radius )
            if r > 0 and not car.isInSafeNode():
                car.speed = r / 2
                speed = min(car.checkColision(config.carCollisionCheckSteps),r/2)
                car.move(speed)
            car.wantsToReserve()
            
    def sanityCheck(self):
        for edge in self.listOfEdges:
            for car1 in edge.carList:
                for car2 in edge.carList:
                    if car1 != car2:
                        if abs(car1.position - car2.position) < car1.radius + car2.radius:
                            raise endOfSanity("blad kolizji samochodow :(")
    """
    Zwraca słownik obrazujący funkcję z tras w trójkę:
    (ile samochodów wjechał, ile samochodów wyjechało, ile czasu samochody 
    spędziły na tej trasie)
    """
    def getStatistics(self):
        returnValue = {}
        for route in self.listOfRoutes:
            returnValue[route] = (route.wjechalo, route.wyjechalo, route.sumaWjechanych )
        return returnValue
